/*
 * Wazuh Module for routing messages.
 * Copyright (C) 2015, Wazuh Inc.
 * May 1, 2023
 *
 * This program is free software; you can redistribute it
 * and/or modify it under the terms of the GNU General Public
 * License (version 2) as published by the FSF - Free Software
 * Foundation.
 */

#include "wm_vulnerability_scanner.h"
#include "config/indexer-config.h"
#include "external/cJSON/cJSON.h"
#include "sym_load.h"
#include "vulnerability_scanner.h"

#ifndef HOST_NAME_MAX
#define HOST_NAME_MAX 255
#endif

static void* wm_vulnerability_scanner_main(wm_vulnerability_scanner_t* data);
static void wm_vulnerability_scanner_destroy(wm_vulnerability_scanner_t* data);
static void wm_vulnerability_scanner_stop(wm_vulnerability_scanner_t* data);
cJSON* wm_vulnerability_scanner_dump(wm_vulnerability_scanner_t* data);

void* vulnerability_scanner_module = NULL;
vulnerability_scanner_start_func vulnerability_scanner_start_ptr = NULL;
vulnerability_scanner_stop_func vulnerability_scanner_stop_ptr = NULL;

const wm_context WM_VULNERABILITY_SCANNER_CONTEXT = {
    .name = "vulnerability_scanner",
    .start = (wm_routine)wm_vulnerability_scanner_main,
    .destroy = (void (*)(void*))wm_vulnerability_scanner_destroy,
    .dump = (cJSON * (*)(const void*)) wm_vulnerability_scanner_dump,
    .sync = NULL,
    .stop = (void (*)(void*))wm_vulnerability_scanner_stop,
    .query = NULL,
};

static void wm_vulnerability_scanner_log_config(cJSON* config_json) {
    if (config_json) {
        char* config_str = cJSON_PrintUnformatted(config_json);
        if (config_str) {
            mtdebug1(WM_VULNERABILITY_SCANNER_LOGTAG, "%s", config_str);
            cJSON_free(config_str);
        }
    }
}

void* wm_vulnerability_scanner_main(wm_vulnerability_scanner_t* data) {
    mtinfo(WM_VULNERABILITY_SCANNER_LOGTAG, "Starting vulnerability_scanner module.");
    if (vulnerability_scanner_module = so_get_module_handle("vulnerability_scanner"), vulnerability_scanner_module) {
        vulnerability_scanner_start_ptr =
            so_get_function_sym(vulnerability_scanner_module, "vulnerability_scanner_start");
        vulnerability_scanner_stop_ptr =
            so_get_function_sym(vulnerability_scanner_module, "vulnerability_scanner_stop");

        // Check for missing configurations. These configurations may miss when using the old deprecated VD config.
        if (!cJSON_GetObjectItem(data->vulnerability_detection, "enabled")) {
            cJSON_AddStringToObject(data->vulnerability_detection, "enabled", "yes");
        }

        if (!cJSON_GetObjectItem(data->vulnerability_detection, "index-status")) {
            cJSON_AddStringToObject(data->vulnerability_detection, "index-status", "yes");
        }

        if (!cJSON_GetObjectItem(data->vulnerability_detection, "feed-update-interval")) {
            cJSON_AddStringToObject(data->vulnerability_detection, "feed-update-interval", "60m");
        }

        if (vulnerability_scanner_start_ptr) {
            cJSON* config_json = cJSON_CreateObject();
            cJSON_AddItemToObject(
                config_json, "vulnerability-detection", cJSON_Duplicate(data->vulnerability_detection, TRUE));
            cJSON_AddNumberToObject(config_json, "wmMaxEps", wm_max_eps);
            cJSON_AddNumberToObject(config_json,
                                    "translationLRUSize",
                                    getDefine_Int("vulnerability-detection", "translation_lru_size", 1, 100000));
            cJSON_AddNumberToObject(
                config_json, "osdataLRUSize", getDefine_Int("vulnerability-detection", "osdata_lru_size", 1, 100000));
            cJSON_AddNumberToObject(config_json,
                                    "remediationLRUSize",
                                    getDefine_Int("vulnerability-detection", "remediation_lru_size", 1, 100000));
            cJSON_AddNumberToObject(config_json,
                                    "managerDisabledScan",
                                    getDefine_Int("vulnerability-detection", "disable_scan_manager", 0, 1));

            if (indexer_config == NULL) {
                cJSON_AddItemToObject(config_json, "indexer", cJSON_CreateObject());
            }
            else {
                cJSON_AddItemToObject(config_json, "indexer", cJSON_Duplicate(indexer_config, TRUE));
            }

            /* Add cluster name to vulnerability detection configurations
             * If the cluster is enabled, the cluster name is the cluster name read from the configuration file.
             * If the cluster is disabled, the cluster name is the hostname, known as the manager name.
             */
            const bool cluster_status = get_cluster_status();
            cJSON_AddBoolToObject(config_json, "clusterEnabled", cluster_status);

            if (cluster_status) {
                char* cluster_name = get_cluster_name();
                cJSON_AddStringToObject(config_json, "clusterName", cluster_name);
                os_free(cluster_name);

                char* manager_node_name = get_node_name();
                cJSON_AddStringToObject(config_json, "clusterNodeName", manager_node_name);
                os_free(manager_node_name);
            }
            else {
                char hostname[HOST_NAME_MAX + 1];
                if (gethostname(hostname, HOST_NAME_MAX) == 0) {
                    cJSON_AddStringToObject(config_json, "clusterName", hostname);
                }
                else {
                    cJSON_AddStringToObject(config_json, "clusterName", "undefined");
                }

                cJSON_AddStringToObject(config_json, "clusterNodeName", "undefined");
            }

            wm_vulnerability_scanner_log_config(config_json);
            vulnerability_scanner_start_ptr(mtLoggingFunctionsWrapper, config_json);
            cJSON_Delete(config_json);
        }
        else {
            mtwarn(WM_VULNERABILITY_SCANNER_LOGTAG, "Unable to start vulnerability_scanner module.");
            return NULL;
        }
    }
    else {
        mtwarn(WM_VULNERABILITY_SCANNER_LOGTAG, "Unable to load vulnerability_scanner module.");
        return NULL;
    }

    return NULL;
}

void wm_vulnerability_scanner_destroy(wm_vulnerability_scanner_t* data) {
    free(data);
}

void wm_vulnerability_scanner_stop(__attribute__((unused)) wm_vulnerability_scanner_t* data) {
    mtinfo(WM_VULNERABILITY_SCANNER_LOGTAG, "Stopping vulnerability_scanner module.");
    if (vulnerability_scanner_stop_ptr) {
        vulnerability_scanner_stop_ptr();
    }
    else {
        mtwarn(WM_VULNERABILITY_SCANNER_LOGTAG, "Unable to stop vulnerability_scanner module.");
    }
}

cJSON* wm_vulnerability_scanner_dump(wm_vulnerability_scanner_t* data) {
    cJSON* root = cJSON_CreateObject();
    cJSON_AddItemToObject(root, "vulnerability-detection", cJSON_Duplicate(data->vulnerability_detection, TRUE));
    cJSON_DeleteItemFromObject(cJSON_GetObjectItem(root, "vulnerability-detection"), "index-status");
    cJSON_DeleteItemFromObject(cJSON_GetObjectItem(root, "vulnerability-detection"), "cti-url");
    cJSON_DeleteItemFromObject(cJSON_GetObjectItem(root, "vulnerability-detection"), "clusterName");

    return root;
}
